package com.van.utils;

import com.van.entity.account.Account;
import org.jose4j.jwa.AlgorithmConstraints;
import org.jose4j.jwk.RsaJsonWebKey;
import org.jose4j.jwk.RsaJwkGenerator;
import org.jose4j.jws.AlgorithmIdentifiers;
import org.jose4j.jws.JsonWebSignature;
import org.jose4j.jwt.JwtClaims;
import org.jose4j.jwt.MalformedClaimException;
import org.jose4j.jwt.consumer.ErrorCodes;
import org.jose4j.jwt.consumer.InvalidJwtException;
import org.jose4j.jwt.consumer.JwtConsumer;
import org.jose4j.jwt.consumer.JwtConsumerBuilder;
import org.jose4j.lang.JoseException;

import java.security.Key;
import java.security.PrivateKey;

/**
 * JWT 加密解密工具
 * @author VanKa qq:2395073039
 * @date 2021/7/16 11:25
 */
public class JwtUtil {
    public static RsaJsonWebKey rsaJsonWebKey = null;

    private static String applicationName = "one-precloues";

    /**
     * 获取 RSA密钥对
     * @param t type
     * @param i id
     * @throws JoseException e
     */
    public static void getKey(int t, int i) throws JoseException {
        //生成一个RSA密钥对，用于签署和验证JWT，包装在JWK中
        rsaJsonWebKey = RsaJwkGenerator.generateJwk(2048);
        rsaJsonWebKey.setKeyId(applicationName);
    }
    /**
     * 生成Token
     * @param type 用户类型
     * @param userId 用户ID
     * @param account 用户账号
     * @param name 用户名
     * @param loseTime 失效的时间
     * @param invalidTime 无效时长
     * @return Token
     * @throws JoseException e
     */
    public static String createToken(int type, int userId, String account,String name, int loseTime, int invalidTime) throws JoseException {

        JwtClaims claims = new JwtClaims();
        claims.setIssuer(applicationName); // 谁创建了令牌并签署了它
        claims.setAudience("user"); // 令牌将被发送给谁
        claims.setExpirationTimeMinutesInTheFuture(loseTime); // 令牌失效的时间长（从现在开始N分钟）
        claims.setGeneratedJwtId(); // 令牌的唯一标识符
        claims.setIssuedAtToNow(); // 当令牌被发布/创建时（现在）
        claims.setNotBeforeMinutesInThePast(invalidTime); // 在此之前，令牌无效（N分钟前）
        claims.setSubject("user");//主题 ,是令牌的对象

        //声明/属性，所有值请都使用String类型
        claims.setClaim("account", account);
        claims.setClaim("userId", userId);
        claims.setClaim("type", type);
        claims.setClaim("name", name);

        JsonWebSignature jws = new JsonWebSignature();
        jws.setAlgorithmHeaderValue(AlgorithmIdentifiers.RSA_USING_SHA256);// 在jw/jws上设置签名算法，该算法将完整性保护声明
        jws.setKeyIdHeaderValue(rsaJsonWebKey.getKeyId()); //设置关键ID（kid）头，因为这是一种礼貌的做法
        jws.setPayload(claims.toJson());//JWS的有效负载是JWT声明的JSON内容
        jws.setKey(rsaJsonWebKey.getPrivateKey());//JWT使用私钥签署
//        jws.setDoKeyValidation(false); // 是否放松密钥长度要求

        //签署JWS并生成紧凑的序列化或完整的jw/JWS 表示
        String token = jws.getCompactSerialization();
        return token;
    }
    /**
     * 生成Token
     *
     * @param key  秘钥
     * @param user 用户
     * @return Token token
     * @throws JoseException e
     */
    public static String createToken(PrivateKey key, Account user) throws JoseException {

        JwtClaims claims = new JwtClaims();
        claims.setIssuer(applicationName); // 谁创建了令牌并签署了它
        claims.setAudience("user"); // 令牌将被发送给谁
        claims.setExpirationTimeMinutesInTheFuture(24 * 50); // 令牌失效的时间长（从现在开始N分钟）
        claims.setGeneratedJwtId(); // 令牌的唯一标识符
        claims.setIssuedAtToNow(); // 当令牌被发布/创建时（现在）
        claims.setNotBeforeMinutesInThePast(5); // 在此之前，令牌无效（N分钟前）
        claims.setSubject("user");//主题 ,是令牌的对象

        //声明/属性，所有值请都使用String类型
        claims.setClaim("phone", user.getPhone());
        claims.setClaim("id", user.getId());
        claims.setClaim("type", user.getType());
        claims.setClaim("status", user.getStatus());
        claims.setClaim("name", user.getName());

        JsonWebSignature jws = new JsonWebSignature();
        jws.setAlgorithmHeaderValue(AlgorithmIdentifiers.RSA_USING_SHA256);// 在jw/jws上设置签名算法，该算法将完整性保护声明
        jws.setKeyIdHeaderValue(applicationName); //设置关键ID（kid）头，因为这是一种礼貌的做法
        jws.setPayload(claims.toJson());//JWS的有效负载是JWT声明的JSON内容
        jws.setKey(key);//JWT使用私钥签署
        jws.setDoKeyValidation(false); // 是否放松密钥长度要求

        //签署JWS并生成紧凑的序列化或完整的jw/JWS 表示
        return jws.getCompactSerialization();
    }

    /**
     * 解析token
     *
     * @param token 加密token
     * @return token token
     * @throws JoseException e
     */
    public static Account analysisToken(Key key, String token) throws JoseException, MalformedClaimException {
        Account user = new Account();
        JwtConsumer jwtConsumer = new JwtConsumerBuilder()
                .setRequireExpirationTime()//JWT必须有一个有效期时间
                .setAllowedClockSkewInSeconds(30) // 允许在验证基于时间的令牌时留有一定的余地，以计算时钟偏差。单位/秒
                .setRequireSubject()  // 主题声明
                .setExpectedIssuer(applicationName) // JWT需要由谁来发布,用来验证 发布人
                .setExpectedAudience("user") // JWT的目的是给谁, 用来验证观众
                .setVerificationKey(key) // 用公钥验证签名 ,验证秘钥
                .setJwsAlgorithmConstraints( // 只允许在给定上下文中预期的签名算法,使用指定的算法验证
                        new AlgorithmConstraints(AlgorithmConstraints.ConstraintType.WHITELIST, // 白名单
                                AlgorithmIdentifiers.RSA_USING_SHA256))
                .build(); // 创建JwtConsumer实例

        try {
            JwtClaims processedClaims = jwtConsumer.processToClaims(token);
            Long id = (Long) processedClaims.getClaimValue("id");
            user.setId(id);
            user.setType((Integer) processedClaims.getClaimValue("type"));
            user.setStatus((Integer) processedClaims.getClaimValue("status"));
            user.setName((String) processedClaims.getClaimValue("name"));
            user.setPhone((String) processedClaims.getClaimValue("phone"));
            return user;
        } catch (InvalidJwtException e) {
            if (e.hasExpired()) {
                System.out.println("JWT expired at " + e.getJwtContext().getJwtClaims().getExpirationTime());
                user.setMsg("用户信息过期了！");
                return user;
            }
            if (e.hasErrorCode(ErrorCodes.AUDIENCE_INVALID)) {
                System.out.println("JWT had wrong audience: " + e.getJwtContext().getJwtClaims().getAudience());
                user.setMsg("用户信息无效了！");
                return user;
            }
            user.setMsg("用户信息解析异常！");
            return user;
        }
    }

}
